Zend - The php Company      |   |   |    
 
 
 
Save $50:

Zend Studio
+
Advanced PHP Training


Now Only: $348 $298!

Expires: Sept. 30th, 2004


PHP 5
PHP 5 Contest
Forums
Resources
Articles
Tutorials
Tips & Tricks
Books
Help Topics
Documentation
Code/App Gallery
Community
Code News
Downloads
Partners
Search

View printable format View printable format
Send this article to a friend Send this article to a friend
Notify me when a new article appears Notify me when a new article appears

Preview How Not To Use A Template System Notify me when a new spotlight appears Send this article to a friend View printable format Next
By Darrell Brogdon

May 27, 2003

Intended Audience
Background
What Is A Template System?
Separating Content and Presentation
Getting The Content
Security Note
Table Templates
Getting the Navigation
Putting It All Together
Conclusion
About the Author

Intended Audience

The purpose of this article is to show PHP’s inherent capabilities as a template engine for developers who don’t wish to learn another sub-language.

Background

My last article didn’t generate many flames so I thought I’d fan the fire a little this time around. With all the new template system projects that seem to be the rage these days, I find myself questioning the need. To my mind PHP is the ultimate template system. In this article I’ll lay out some ideas on how to properly use PHP so that you simply won’t need another template system.

What Is A Template System?

Shortly after Netscape released their browser versions with all its amazing bells and whistles (inline graphics, background images, ...COLOR!), web developers and designers found themselves in a bit of a quandary. How to separate the presentation from the content? Without some way of dynamically generating the content the designer might have to create a lot of versions of the site for each page. Tedious even when creating the site, and even more so when a change needed to be made. A change to “my_cool_pic.jpg" on one page meant you had to change it on all of them, by hand!

Remember, this was before the days of Dreamweaver. Notepad just doesn’t cut it (unless you’re a sadist).

So the designers turned to the developers and demanded that they come up with a way of generating the pages dynamically, on-the-fly. Developers like a good problem needing to be solved when they see it, so they dove right in with Perl (Perl was better than C for such things, and PHP hadn’t been invented yet).

Perl being - well - Perl, the developers soon found that there was still much room for improvement. So they came up with things like FastTemplates. The idea was to chop the page up into its component parts (header, footer, navigation, body, etc...), putting it all back together and dynamically including the content when the page was called.

This was fine up to a point, but designers know Photoshop and HTML, and don’t care much for curly braces and command-lines. Conversely, developers know curly braces but don’t care so much for Photoshop, and grudgingly tolerate HTML as one of “those" languages. When a designer decided that the background color of the header navigation had a bit too much mauve (who names these colors anyway?) she might not be able to simply edit an HTML file. The use of Vi or Emacs might be involved <insert scary violin noises here>. So the designer would ask the developer to make the change for her, and the developer would promptly quit in a huff and get another job making $20,000 more a year. Ah, the good old days...

Anyway, not long after this dance had played itself out, a buzz about PHP was beginning to stir. You mean you can mix the code and HTML? Yep, and you can talk to a lot of databases, and it looks a lot like C. Curly braces and everything!

Not what the designers wanted to hear. Lets face it: designers want to design things and developers want to write code.

Separating Content and Presentation

So out pop projects like FastTemplates for PHP and Smarty whose real purpose is (or should be) to separate content from presentation. The only problem with these projects is that designers still have to be developers. They still have to learn the language that the template system implements.

For example, using Smarty you can include a file like so:

{include file ="my_template.tpl" }

Or you could use PHP itself to include the file:

<?php  include("my_template.tpl" ); ?>

There’s not much difference between the two methods, in this example it’s just a matter of an extra three characters. Well, from a designer’s standpoint anyway, and the PHP option at least looks somewhat like an HTML tag (with ‘<’ and ‘>’).

Including files isn’t nearly as off-putting as using conditionals, however. Let’s see a conditional using Smarty. (By the way, I’m not picking on Smarty for any particular reason other than it seems to be the most popular of PHP-based template systems. The price of fame and all that.)

{if $email eq "john@doe.com"}
    
Hello <b>John Doe</b>.
{/if}

And the same in PHP:

<?php
if ($email == "john@doe.com") {
    echo 
"Hello <b>John Doe</b>";
}
?>

Again, little difference but both are enough to make the designer throw fits.

So in order to help appease both the designers and developers I would like to suggest a solution that I’ve found to be very simple yet very effective.

Working with a good designer you can structure the layout of the site to be conducive for using as a template without being elaborate or complex. Lets say you have a site that requires a Header, Footer, Navigation, and Content sections. It might look something like this:

Header
Nav Content
Footer

In this scenario the likelihood of the Header and Footer changing from page to page is probably less than the Nav and Content. However, we still want to keep them all as separate components. The HTML for this page might look something like this:

<html>
<
head>
    <
title>My Web Site</title>
</
head>
<
body>
<
table>
    <
tr>
        <
td colspan="2" height="15%";>
            
<!-- HEADER -->
        </
td>
    </
tr>
    <
tr>
        <
td width="25%" height="80%">
            
<!-- NAVIGATION -->
        </
td>
        <
td width="75%" height="80%">
            
<!-- CONTENT -->
        </
td>
    </
tr>
    <
tr>
        <
td colspan="2" height="5%">
            
<!-- FOOTER -->
        </
td>
    </
tr>
</
table>
</
body>
</
html>

Since this page is generated dynamically, we need some way of determining which content to display in the Content section. One of the simplest ways is using URL parameters. So lets say we want to display an “About" page with content containing information about our web site, using a URL like this:

http://example.com/index.php?page=about

Getting The Content

We can now use the following PHP code in the Content section to properly display the About page:

<td width="75%" height="80%">
    
<!-- CONTENT -->
    <?
php
    
if ($_GET['page'] == 'about') {
        include(
'about.php');
    }    
    ?>
</td>

Or, better yet:

<td width="75%" height="80%">
    
<!-- CONTENT -->
    
<?php
    
// Assume the value of $_GET['page'] is the same
    // as the file name we’re going to include.
    
include($_GET['page'] . '.php');
    ?>
</td>

This method has one major problem and that is, how do we include the Home page when $_GET['page'] has no value? Using a conditional, we can determine if there is a value for $_GET['page'] and include the home page if there isn’t a value.

<td width="75%" height="80%">
    
<!-- CONTENT -->
    
<?php
    
if (empty($_GET['page'])) {
        include(
'home.php');    
    } else {
        include(
$_GET['page'] . '.php');
    }
    ?>
</td>

As good as this might be, its still doesn’t meet the criteria of separation of content and presentation. There is still way too much code for the designer’s taste, and it would be best contained within a function. So lets put it all inside a function, along with a few additional features that might make life simpler for the designer.

Security Note

You should be aware that this method of determining the content based on a $_GET value is inherently dangerous. It is very easy for someone to change the value and thus be able to view a page you might not want them to see. Make sure you validate any $_GET values before doing anything with them!

<?php
function getContent() {
    if (empty(
$_GET['page'])) {
        
$page = 'home';
    } else {
        
$page = $_GET['page'];
    }

    
$page .= '.php';
    
    if (
false == is_file($page)) {
        
$page = 'file_not_found.php';
    }

    include(
$page);
}
?>

In addition to automatically including either a named content or home page, the above function also performs a few additional duties:
  • First it’s cleaner and somewhat more efficient in that there is only one “include()" call made.
  • Second, it checks to see that the requested page actually exists as a file. For example, if you were to call this function with “page=foobar" and there wasn’t a “foobar.php" file in that directory, then it would default to a file named “file_not_found.php" (which should contain an alert to the user that the file they requested wasn’t found). Alternatively you could push out an HTTP 404 error, if that was your preference (remember to use Output Buffering if you do).
Now that we have encapsulated all of this inside a function let’s see how it might look to the designer:

<td width="75%" height="80%">
    
<!-- CONTENT -->
    
<?php getContent(); ?>
</td>

In addition to reducing the code from ten lines to four we’ve made it much simpler from the designer’s point of view. Granted, “<?php getContent(); ?>" does look a bit odd compared to most HTML code, but you can learn to live with it.

You might be wondering how we got access to $_GET from inside the function without defining it as global or passing it as a function parameter. $_GET along with $_POST, $_FILES, etc... are considered “superglobals" meaning these arrays retain their scope no matter where in the script they are referenced. Even inside functions.

Table Templates

Another problem most people will run into is how to display dynamic table data in a template. Anyone who has done this in PHP knows that it can get messy.

Lets assume we have “page=members_list" in our URL string, meaning we’re going to be including a file called “members_list.php". This page will contain a table that lists miscellaneous information about a number of members such as name, telephone number, etc... The data will be pulled from a database of course and we need to make it so that the designer can design the table and the developer can build the logic.

So an example of the table from the designer’s point of view might look something like this:

<table border="1">
    <
th>ID</th>
    <
th>First Name</th>
    <
th>Last Name</th>
    <
th>Email</th>
    <
th>Telephone</th>
    <?
php getMembersList('Silver', 'White'); ?>
</table>

And here is the function definition for getMembersList():

<?php
function getMembersList($bgcolor1, $bgcolor2) {
    
// ...
    // SQL Query
    // ...

    
$i = 0;

    while (
$row = mysql_fetch_array($result)) {
        
$bgcolor = ($i++ & 1) ? $bgcolor1 : $bgcolor2;
        
        echo 
'<tr>';
        echo 
'<td bgcolor=&#8221;' . $bgcolor . '">' 
        
.  $row['first_name'] 
        . 
'</td>';
        echo 
'<td bgcolor=&#8221;' . $bgcolor . '">' 
        
.  $row['last_name'] 
        . 
'</td>';
        echo 
'<td bgcolor=&#8221;' . $bgcolor . '">' 
        
.  $row['email'] 
        . 
'</td>';
        echo 
'<td bgcolor=&#8221;' . $bgcolor . '">' 
        
.  $row['telephone'] 
        . 
'</td>';
        echo 
'</tr>';
    }
}
?>

This function takes as its parameters two colors that will be used for alternating table data background colors. Then it performs a SQL query on the database to get the members data. Finally it walks through the resulting array returned by mysql_fetch_array() and using echo() displays the HTML formatted member data.

Getting the Navigation

Now that we’ve simplified getting the content and seen how to build a table template, lets have a look at the navigation template.

The basic structure is virtually identical to getContent() except that with navigation there are probably some things that we want to show/hide depending on factors such as whether a user is logged in. Lets assume that the following are the menu items we require:
  • Home
  • About
  • Contact
  • Log In/Log Out
If a user is logged out then it should say “Log In". Otherwise it should say “Log Out". In addition, if a user is logged in we want to show the following navigation items:
  • My Account
  • Members Directory
All of this will be contained within a function called getNav() and it looks like this:

<?php
function getNav() {
    echo 
'<a href="/index.php">Home</a><br/>';
    echo 
'<a href="/index.php?page=about">About</a><br/>';
    echo 
'<a href="/index.php?page=contact">Contact</a><br/>';

    if (empty(
$_COOKIE['user_id'])) {
        echo 
'<a href="/index.php?page=login">Log In</a><br/>';
    } else {
    echo 
'<a href="/index.php?page=logout">Log Out</a><br/>';
    echo 
'<a href="/index.php?page=my_account">My Account</a><br/>';
    echo 
'<a href="/index.php?page=members_directory">Members Directory</a><br/>';
    }
}
?>

In this function we’re mainly just using echo() to display the hyperlinks. In addition, we’re checking whether a user is logged in (assuming their user ID is stored in a cookie if they are) and displaying the appropriate links.

As with $_GET, $_COOKIE is a superglobal so its automatically in scope to the getNav() function.

Putting It All Together

Now that we have templates for the navigation and content areas of the page lets see how the modified HTML might look:

<html>
<
head>
    <
title>My Site :<?php getTitle(); ?></title>
</
head>
<
body>
<
table>
    <
tr>
        <
td colspan="2" height="15%">
            
<!-- HEADER -->
            
<?php getHeader(); ?>
        
</td>
    </
tr>
    <
tr>
        <
td width="25%" height="80%">
            
<!-- NAVIGATION -->
            
<?php getNav(); ?>
        
</td>
        <
td width="75%" height="80%">
            
<!-- CONTENT -->
            
<?php getContent(); ?>
        
</td>
    </
tr>
    <
tr>
        <
td colspan="2" height="5%">
            
<!-- FOOTER -->
            
<?php getFooter(); ?>
        </td>
    </
tr>
</
table>
</
body>
</
html>

In addition to functions for getting the Header and Footer, you probably noticed one other function; getTitle(). Since we’re determining which page to include with getContent() its safe to assume that we would want to update the page title accordingly. The getTitle() function does just that:

<?php
    
function getTitle() {
        echo 
ucfirst($_GET['page']);
}
?>

This is a simple function that displays the value of the “page" argument after capitalizing the first letter. You’ll probably want to get fancier with your own version.

Conclusion

In this article you have seen how to structure your web site so that you can utilize the power of PHP as a template system.

Some of you developers out there are probably groaning because you have to write HTML code. When you get back from whatever alternate universe doesn’t require you to use HTML when developing PHP applications I hope you’ll have brought me a t-shirt. Lets face it: most PHP applications are used for the web, and the language of the web is HTML.

If you’re interested in seeing a good, working, real-world example of using PHP templates check out the b2 blogging system. Specifically, the ‘b2template.php’ file. They have a rich API for use in any custom templates you might build and do a reasonably good job of separating content from presentation (though they do let a little too much PHP inside b2template.php for my tastes).

While I hope you have learned something that you can use in your own projects, this tutorial by no means covers all that there is to web site templates. I encourage you to try out these techniques and post any ideas and suggestions you have in the comments section.

About the author

Darrell Brogdon is an independent consultant specializing in web application development. He can be reached at darrell@brogdon.net.

Readers' Comments View notes only

Begin a new thread

+ Expand tree

    Script and Content Seperation
Randall Augustus Alexander
27/05/2003 12:08
    Designers and Templates...
Nuno Ferreira
27/05/2003 14:31
    Templating??
Mathias Vestergaard
27/05/2003 18:55
    Templates have to be done the right way...
Patrick Desmarais
27/05/2003 20:47
    What does Zend know about content management?
Brian
27/05/2003 22:10
    Separating Content and Presentation means also...
Plop
27/05/2003 22:36
    April fools?
Paul
28/05/2003 03:04
    how not to use a templating system?
vance
28/05/2003 08:12
    Functions don't quite qualify as "templating"
Chris
28/05/2003 18:08
    My disbelief about this article
M. Sokolewicz
28/05/2003 19:23
    That isn't separating code from content
Blaine
29/05/2003 05:17
    php included into html or html included in php ?
Michel
03/06/2003 11:10
    Template debate
Mike Norris
11/07/2003 18:13
    REAL NICE STUFF- I LIKE TH
Florian
03/08/2003 01:25
    REAL NICE STUFF- I LIKE THIS
Florian
03/08/2003 01:28
    Deathtrap?
N
03/08/2003 15:10
    I'll bite... what is the meaning of the article?
BillR
07/08/2003 00:37
    optimized function
catalin
14/09/2003 02:06
    optimized function
catalin
14/09/2003 02:08
    I've enjoyed this one.
Anders
19/10/2003 04:38
    my version
Aitrus
29/10/2003 19:21
    won't work if ftp access to templates is needed...
LGW
04/03/2004 19:11
    Templates
Barry
12/03/2004 13:33